﻿package Util
{
	
	/**
	 * ...
	 * @author Lissa Avery
	 */
	public class PerlinNoise 
	{
		private static var noise:Array;
		private static var perlinNoise:Array;
		
		public static function getBrownianMotion(x:int, y:int, octaves:Number):Number {
			var fValue:Number = 0.0;
			var fWeight:Number = 1.0;
			var i:int;
			var j:int;

			var fTemp:Array = new Array(2);
			fTemp[0] = x;
			fTemp[1] = y;

			for(i=0; i < octaves; i += 1)
			{
				fValue += PerlinNoise.getIntPerlinNoise(fTemp[0], fTemp[1], octaves) * fWeight; // Sum weighted noise value
				//show_debug_message("P-noise at (" + string(fTemp[0]) + ", " + string(fTemp[1]) + "): " + string(get_int_perlin_noise(fTemp[0], fTemp[1], 50)));
				fWeight *= 0.75; // Adjust the weight
				for(j=0; j < 2; j += 1)
				{
				   fTemp[j] *= 2.0;
				}
			}

			return fValue;
		}
		
		/**
		 * This function returns a perlin_noise value converted to an integer in the range [0, n).
		 * The function takes three parameters: the x-coordinate, the y-coordinate, and the value n.
		 */
		private static function getIntPerlinNoise(x1:int, y1:int, k:Number):int {
			var n:Number;
			n = PerlinNoise.getPerlinNoise(x1, y1);
    
			return Math.floor(n * k);
		}
		
		/**
		 * This function returns a Perlin noise value at the location (x, y). 
		 * This function takes two parameters - the x-coordinate (an integer) 
		 * and a y-coordinate (an integer).
		 */
		private static function getPerlinNoise(x:int, y:int):Number {			
			return perlinNoise[x][y];
		}
		
		/**
		 * Call this function first. This creates the array with perlin noise, 
		 * which is used by the functions get_perlin_noise and get_int_perlin_noise.
		 * 
		 * This function takes two parameters - the width and height of the grid to 
		 * fill with perlin_noise.
		 */
		public static function initNoise(maxWidth:int, maxHeight:int):void {
			noise = new Array(maxWidth);
			var noise_falloff:Number;
			perlinNoise = new Array(maxWidth);
			
			var i:int;
			var j:int;
			var k:int;
    
			//create base noise.
			for(i = 0; i < maxWidth; i += 1)
			{
				noise[i] = new Array(maxHeight);
				for(j = 0; j < maxHeight; j += 1)
				{
					noise[i][j] = Math.random();
				}
			}
    
			var smoothnoise:Number;
			var sample_period:Number;
			var amplitude:Number;
			var total_amplitude:Number;
			var octave_count:Number;
			
			octave_count = 6;
        
			for(i = 0; i < maxWidth; i += 1)
			{
				perlinNoise[i] = new Array(maxHeight);
				for(j = 0; j < maxHeight; j += 1)
				{
					smoothnoise = 0;
					sample_period = 64;
					amplitude = 0.5;
					total_amplitude = 0;
					
					for (k = 0; k < octave_count; k += 1)
					{
						total_amplitude += amplitude;
						smoothnoise += PerlinNoise.getNoise(i, j, sample_period, maxHeight, maxWidth) * amplitude;
						
						sample_period/= 2;
						amplitude /= 2;
					}
					
					
					perlinNoise[i][j] = smoothnoise / total_amplitude;
					//show_debug_message("Perlin noise: " + string(perlin_noise[i, j]));
				}
			}
		}
		
		/**
		 * This function returns smoothnoise. It takes three parameters: 
		 * the x-coodrinate, the y-coordinate, and the sample_period.
		 * 
		 * @param	x1
		 * @param	y1
		 */
		private static function getNoise(x1:int, y1:int, sample_period:Number, maxHeight:int, maxWidth:int ):Number {    
			var xx:Number;
			
			xx = Math.floor(x1 / sample_period);
			
			var noiseX1:Number, noiseX2:Number;
			
			noiseX1 = xx * sample_period;
			noiseX2 = (xx + 1) * sample_period;
			
			var yy:int;
			
			yy = Math.floor(y1 / sample_period);
			
			var noiseY1:Number, noiseY2:Number;
			
			noiseY1 = yy * sample_period;
			noiseY2 = (yy + 1) * sample_period;
			
			var n1:Number, n2:Number, n3:Number, n4:Number;
			
			n1 = noise[noiseX1][noiseY1]; 
			n2 = noise[noiseX1][noiseY2 % maxHeight]; 
			n3 = noise[noiseX2 % maxWidth][noiseY1]; 
			n4 = noise[noiseX2 % maxWidth][noiseY2 % maxHeight];
				
			var pp:Number, px:Number, py:Number, xy:Number;
			
			pp = sample_period*sample_period;
			px = sample_period*(x1 - noiseX1);
			py = sample_period*(y1 - noiseY1);
			xy = (x1 - noiseX1)*(y1-noiseY1);

			var noiseTmp:Number;
			
			//bilinear interpolation.
			noiseTmp = (pp - px - py + xy) * n1 + (py - xy)*n2 + (px - xy)*n3 + xy*n4;
			
			return noiseTmp / pp;
		}
		
	}
	
}